package com.luckyframe.project.jmeter.jmeterTestPlan.domain;

import cn.hutool.core.util.XmlUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.luckyframe.common.netty.Result;
import com.luckyframe.common.netty.jmeter.JmeterNetty;
import com.luckyframe.common.utils.StringUtils;
import com.luckyframe.common.utils.spring.SpringUtils;
import com.luckyframe.framework.config.JmeterConfig;
import com.luckyframe.project.jmeter.execution.JmeterExecutor;
import com.luckyframe.project.jmeter.execution.testcase.TestCaseListener;
import com.luckyframe.project.jmeter.jmeterJarMgt.domain.JmeterJarMgt;
import com.luckyframe.project.jmeter.jmeterJarMgt.service.IJmeterJarMgtService;
import com.luckyframe.project.jmeter.jmeterNode.domain.JmeterNode;
import com.luckyframe.project.jmeter.jmeterNode.service.IJmeterNodeService;
import com.luckyframe.project.jmeter.jmeterTestCase.domain.DependencyFile;
import com.luckyframe.project.jmeter.jmeterTestCase.domain.JmeterTestCase;
import com.luckyframe.project.jmeter.jmeterTestPlan.context.CaseRunningContext;
import com.luckyframe.project.jmeter.jmeterTestPlan.context.TestPlanListener;
import com.luckyframe.project.jmeter.jmeterTestPlanRecord.domain.JmeterTestPlanRecord;
import com.luckyframe.project.jmeter.jmeterTestPlanRecord.enums.JmeterPlanRecordStatus;
import com.luckyframe.project.jmeter.jmeterTestPlanRecord.service.IJmeterTestPlanRecordService;
import org.apache.commons.cli.avalon.CLOption;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import java.io.File;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;

public class PlanCaseTask  {

    private static final Logger log = LoggerFactory.getLogger(PlanCaseTask.class);

    private IJmeterNodeService jmeterNodeService= SpringUtils.getBean(IJmeterNodeService.class);

    private IJmeterTestPlanRecordService jmeterTestPlanRecordService= SpringUtils.getBean(IJmeterTestPlanRecordService.class);

    private IJmeterJarMgtService jmeterJarMgtService= SpringUtils.getBean(IJmeterJarMgtService.class);

    private JmeterNetty nettyServer= SpringUtils.getBean(JmeterNetty.class);;

    private JmeterConfig jmeterConfig;

    private JmeterTestPlan plan;

    private JmeterTestPlanRecord planRecord;

    private TestPlanListener testPlanListener;

    private JmeterTestCase testCase;

    private TestCaseListener caseListener;


    public PlanCaseTask(JmeterConfig jmeterConfig, JmeterTestPlan plan,
                        TestPlanListener testPlanListener, JmeterTestCase testCase) {
        this.jmeterConfig = jmeterConfig;
        this.plan = plan;
        this.testPlanListener = testPlanListener;
        this.testCase = testCase;
        this.caseListener=new TestCaseListener(plan,testCase, testPlanListener);

    }

    public void run(PlanCaseTaskGroup group) {

        try {

            List<JmeterNode> jmeterNodes = jmeterNodeService.selectByIdList(JSON.parseArray(testCase.getNodeIds(), Long.class));

            //下载依赖包
            handleDependencyFiles(testCase,new File(testCase.getScriptLocation()),jmeterNodes);

            CaseRunningContext caseRunningContext = buildCaseRunningContext(group,jmeterNodes);

            log.info("plan {} case {} start @time={}",plan.getName(),testCase.getCaseName(), LocalDateTime.now());

            caseListener.setCaseTaskGroup(group);
            JmeterExecutor.INSTANCE.startNonGui(caseRunningContext,caseListener);

        }catch (Exception e){

            if(planRecord!=null){
                updateRecordFail(planRecord,e);
            }

           log.error("plan{} case {} 出错",plan.getName(),testCase.getCaseName(),e);
            // notify the next to execute..
            try {

                group.caseFailed(e);

                //notify the whole plan error and stop
                caseListener.testFailed(e);

            }catch (Exception ex){
                //ignore ....
            }
        }
    }



    private CaseRunningContext buildCaseRunningContext(PlanCaseTaskGroup group, List<JmeterNode> jmeterNodes) {

        String allHost = jmeterNodes.stream().map(JmeterNode::getIp).collect(Collectors.joining(","));
        CLOption option=new CLOption(allHost);

        planRecord = jmeterTestPlanRecordService.saveRecord(plan, testCase, JmeterPlanRecordStatus.EXECUTING);

        CaseRunningContext caseRunningContext=new CaseRunningContext();
        caseRunningContext.setJmeterConfig(jmeterConfig);
        caseRunningContext.setPlanId(plan.getId());
        caseRunningContext.setCaseId(testCase.getId());
        caseRunningContext.setRecordId(planRecord.getId());
        caseRunningContext.setHostOpetion(option);
        caseRunningContext.setSciptPath(testCase.getScriptLocation());
        caseRunningContext.setTestCase(testCase);

        return caseRunningContext;
    }


    private void updateRecordFail(JmeterTestPlanRecord planRecord, Exception e) {

        planRecord.setStatus(JmeterPlanRecordStatus.FAIL.getCode());
        planRecord.setEndTime(new Date());
        planRecord.setErrorMsg(e.getMessage());
        jmeterTestPlanRecordService.updateJmeterTestPlanRecord(planRecord);

    }



    public JmeterTestPlan getPlan() {
        return plan;
    }

    public JmeterTestCase getTestCase() {
        return testCase;
    }

    private void handleDependencyFiles(JmeterTestCase testCase, File script, List<JmeterNode> runningNodes) {

        Document jmx = XmlUtil.readXML(script);
        Node jarNode = XmlUtil.getNodeByXPath("//stringProp[@name='TestPlan.user_define_classpath']", jmx);

        List<DependencyFile> jarDepenentFiles = new ArrayList<>();


        JmeterJarMgt param=new JmeterJarMgt();
        param.setGlobal(1);
        List<JmeterJarMgt> globalJars = jmeterJarMgtService.selectJmeterJarMgtList(param);

        if(CollectionUtils.isNotEmpty(globalJars)){
            for (JmeterJarMgt globalJar : globalJars) {

                DependencyFile dependencyFile = new DependencyFile();
                dependencyFile.setFilename(globalJar.getName());
                dependencyFile.setFileLocation(globalJar.getLocation());
                dependencyFile.setDestDir("lib\\ext");
                jarDepenentFiles.add(dependencyFile);
            }
        }


        if (jarNode != null) {
            String jarDesc = jarNode.getTextContent();
            if (StringUtils.isNotEmpty(jarDesc)) {
                List<String> jarNames = Stream.of(jarDesc.split(",")).map(t -> FilenameUtils.getName(t)).collect(toList());
                for (String jarName : jarNames) {
                    JmeterJarMgt jmeterJarMgt = jmeterJarMgtService.selectJarByName(jarName);
                    if (jmeterJarMgt == null) {
                        throw new RuntimeException("请先上传" + jarName);
                    }

                    DependencyFile dependencyFile = new DependencyFile();
                    dependencyFile.setFilename(jmeterJarMgt.getName());
                    dependencyFile.setFileLocation(jmeterJarMgt.getLocation());
                    dependencyFile.setDestDir("lib\\ext");
                    jarDepenentFiles.add(dependencyFile);
                }
            }
        }


        //testCase依赖的jar包
        List<JmeterJarMgt> caseRelJars = jmeterJarMgtService.selectJarsByTestCaseId(testCase.getId());
        if (CollectionUtils.isNotEmpty(caseRelJars)) {
            for (JmeterJarMgt caseRelJar : caseRelJars) {

                DependencyFile dependencyFile = new DependencyFile();
                dependencyFile.setFilename(caseRelJar.getName());
                dependencyFile.setFileLocation(caseRelJar.getLocation());
                dependencyFile.setDestDir("lib\\ext");
                jarDepenentFiles.add(dependencyFile);

            }
        }


        String additionFiles = testCase.getAdditionFiles();
        if (StringUtils.isNotEmpty(additionFiles)) {
            List<DependencyFile> dependencyFiles = JSON.parseArray(additionFiles, DependencyFile.class);
            for (DependencyFile dependencyFile : dependencyFiles) {
                dependencyFile.setDestDir("bin" + File.separator + "testdata");
                jarDepenentFiles.add(dependencyFile);
            }
        }

        if (CollectionUtils.isNotEmpty(testCase.getDependencyFiles())) {
            for (DependencyFile dependencyFile : testCase.getDependencyFiles()) {
                String fileLocation = dependencyFile.getFileLocation();
                File fileitem = new File(fileLocation);
                if (!fileitem.exists()) {
                    log.error("jmx依赖文件不存在 testCase={} location={}", testCase.getCaseName(), fileLocation);
                    throw new RuntimeException("jmx依赖文件:" + fileLocation + "不存在");
                }
            }
        }


        if (CollectionUtils.isNotEmpty(jarDepenentFiles)) {
            for (JmeterNode runningJmeter : runningNodes) {

                JSONObject preStartCheckMsg = new JSONObject();
                preStartCheckMsg.put("dependencyFiles", jarDepenentFiles);
                preStartCheckMsg.put("command", "preStartCheckMsg");

                Result result = nettyServer.writeMsgSync(preStartCheckMsg.toJSONString(), runningJmeter.getId());
            }
        }
    }
}
